/**
 ** @author:	   浓咖啡
 ** @date:	   2017.4.6
 ** @brief:      原始套接字方式
 */
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/if_arp.h>
#include <linux/filter.h>
#include "cap_manager.h"
#include "stddef.h"

static int sock_fd;

// tcpdump -dd -s 0 "src host 192.168.1.254 && dst port 8888"生成
struct sock_filter BPF_code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 12, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 0, 10, 0xc0a801fe },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 6, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 4, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x000022b8 },
{ 0x6, 0, 0, 0x0000ffff },
{ 0x6, 0, 0, 0x00000000 },
};

/**
 * @brief 设置网卡混杂模式开关
 * @param intf_name
 * @param on 1:开 0：关
 */
static void set_card_promisc(char *intf_name, int on)
{
    struct ifreq ifr;

    strncpy(ifr.ifr_name, intf_name, strlen(intf_name) + 1);

    if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
        perror("ioctl err");
        exit(-1);
    }

    if(on == 1){
        ifr.ifr_flags |= IFF_PROMISC;
    }else{
        ifr.ifr_flags &= ~IFF_PROMISC;
    }

    if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
        perror("ioctl err");
        exit(-1);
    }
}

/**
 * @brief 对于原始套接字，先定死过滤条件
 * @param dev_str
 * @param filter_str
 * @return
 */
static bool raw_init(char *dev_str, char *filter_str)
{
    struct sock_fprog sock_filter;
    sock_filter.len = 15;
    sock_filter.filter = BPF_code;

    sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock_fd == -1){
        perror("sock err");
        return fail;
    }

    setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &sock_filter, sizeof(sock_filter));
    set_card_promisc("eth0", 1);

    return success;
}


void do_frame(int sock)
{
    unsigned char frame_buf[2000];
    int recv_num;
    struct sockaddr src_addr;
    socklen_t addrlen;

    addrlen = sizeof(src_addr);
    bzero(frame_buf, sizeof(frame_buf));
    recv_num = recvfrom(sock, frame_buf, sizeof(frame_buf), 0, &src_addr, &addrlen);

    if(recv_num > 0){
        code(frame_buf, recv_num);
    }
}

static void raw_start()
{
    //线程更好
    while(1) {
        do_frame(sock_fd);
        usleep(100);
    }
}

static void raw_close()
{
    close(sock_fd);
    set_card_promisc("eth0", 0);
}

static cap_opt_t raw_opt = {
    .name = "raw",
    .cap_init = raw_init,
    .start_cap = raw_start,
    .close_cap = raw_close,
};

int init_raw(void)
{
    return register_cap_opt(&raw_opt);
}

